package com.chinatechstar.file.service.impl;

import com.chinatechstar.admin.client.SysDictServiceClient;
import com.chinatechstar.component.commons.service.AliyunOssService;
import com.chinatechstar.component.commons.utils.UuidUtil;
import com.chinatechstar.file.entity.CerAttachmentInfo;
import com.chinatechstar.file.mapper.CerAttachmentInfoMapper;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;

import com.chinatechstar.account.client.SysUserDetailServiceClient;
import com.chinatechstar.admin.client.SysRoleServiceClient;
import com.chinatechstar.component.commons.result.PaginationBuilder;
import com.chinatechstar.component.commons.utils.CollectionUtils;
import com.chinatechstar.component.commons.utils.CurrentUserUtils;
import com.chinatechstar.component.commons.utils.SequenceGenerator;
import com.chinatechstar.file.mapper.FileMapper;
import com.chinatechstar.file.service.FileService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;

/**
 * 文件信息的业务逻辑实现层
 *
 * @版权所有 上海至界信息科技有限公司
 */
@Service
@Transactional
@Slf4j
public class FileServiceImpl implements FileService {

    private static SequenceGenerator sequenceGenerator = new SequenceGenerator();

    @Autowired
    private SysUserDetailServiceClient sysUserDetailServiceClient;
    @Autowired
    private SysRoleServiceClient sysRoleServiceClient;
    @Autowired
    private FileMapper fileMapper;
    @Autowired
    private SysDictServiceClient sysDictServiceClient;
    @Autowired
    private CerAttachmentInfoMapper cerAttachmentInfoMapper;

    @Autowired
    private AliyunOssService aliyunOssService;

    @Value("${studio.profile-upload-dir}")
    private String profileUploadDir;
    @Value("${studio.image-upload-dir}")
    private String imageUploadDir;
    @Value("${studio.image-server-baseurl}")
    private String imageServerBaseurl;
    @Value("${studio.cer-datainit-file-in-disk}")
    private String cerDataInitFileInDisk;
    @Value("${studio.cer-detailreport-file-in-disk}")
    private String cerDetailReportFileInDisk;
    @Value("${studio.cer-endoscopicdiagnosis-file-in-disk}")
    private String cerEndoscopicDiagnosisFileInDisk;
    @Value("${studio.cer-report-file-in-disk}")
    private String cerReportFileInDisk;

    @Value("${studio.cer-attachment-in-disk}")
    private String cerAttachmentInDisk;

    /**
     * 查询文件分页
     */
    @Override
    public Map<String, Object> queryFile(Integer currentPage,
                                         Integer pageSize,
                                         String originalFilename,
                                         String sorter) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("originalFilename", originalFilename);
        paramMap.put("tenantCode",
                     CurrentUserUtils
                         .getOAuth2AuthenticationDetailsInfo()
                         .get("tenantCode"));// 当前用户的租户编码
        if (StringUtils.isNotBlank(sorter)) {
            String sort = sorter.substring(0, sorter.lastIndexOf('_'));
            String sequence =
                "ascend".equals(sorter.substring(sorter.lastIndexOf('_') + 1)) ? "ASC" : "DESC";
            paramMap.put("sort", sort);
            paramMap.put("sequence", sequence);
        } else {
            paramMap.put("sort", "createTime");
            paramMap.put("sequence", "DESC");
        }
        Page<Object> page = PageHelper.startPage(currentPage, pageSize);
        List<LinkedHashMap<String, Object>> resultList = fileMapper.queryFile(paramMap);

        String roleData = sysRoleServiceClient.queryRoleData("uploaddownload",
                                                             CurrentUserUtils
                                                                 .getOAuth2AuthenticationDetailsInfo()
                                                                 .get("name"));
        String[] roleDataArray = roleData == null ? null : roleData.split(",");
        if (roleDataArray != null && roleDataArray.length > 0) {// 处理数据权限
            return PaginationBuilder.buildResult(CollectionUtils.convertFilterList(resultList,
                                                                                   roleDataArray),
                                                 page.getTotal(),
                                                 currentPage,
                                                 pageSize);
        } else {
            return PaginationBuilder.buildResult(resultList,
                                                 page.getTotal(),
                                                 currentPage,
                                                 pageSize);
        }
    }

    @Override
    public List<LinkedHashMap<String, Object>> queryFileById(String fileId) {
        List<LinkedHashMap<String, Object>> linkedHashMaps = fileMapper.queryFileById(fileId);
        return linkedHashMaps;
    }


    /**
     * 删除文件
     */
    @Override
    public void deleteFile(Long[] id) throws IOException {
        List<String> urlList = fileMapper.queryUrlList(id);
        File rootDirectoryPath = new File(ResourceUtils
                                              .getURL("classpath:")
                                              .getPath());
        for (int i = 0; i < urlList.size(); i++) {
            String path = rootDirectoryPath + urlList.get(i);
            Files.deleteIfExists(Paths.get(path));// 删除磁盘里的文件
        }
        fileMapper.deleteFile(id); // 删除数据库文件记录
    }


    /**
     * 上传文件
     */
    @Override
    public void uploadFile(MultipartFile file, Long id, String uploadType) throws IOException {
        String fileName = DateFormatUtils.format(new Date(), "yyyyMMddHHmmssSSS") + file
            .getOriginalFilename()
            .substring(file
                           .getOriginalFilename()
                           .lastIndexOf('.'));
        saveFile2Disk(file, generatePath(fileName));

        if ("avatar".equals(uploadType)) {
            sysUserDetailServiceClient.updateAvatar("/api/file/static/upload/" + fileName, id);
        }

        com.chinatechstar.file.entity.File entity = getFileEntity(file,
                                                                  fileName,
                                                                  "/static/upload/");
        if ("subscribeLogo".equals(uploadType)) {
            List<LinkedHashMap<String, Object>> list = fileMapper.queryFileSysUserByFileId(String.valueOf(
                id));
            if (list.size() > 0) {
                fileMapper.deleteFile(new Long[]{id});
            }
            fileMapper.insertFileSysUser(sequenceGenerator.nextId(), entity.getId(), id);
            fileMapper.insertFile(entity);
        } else {
            fileMapper.insertFileSysUser(sequenceGenerator.nextId(), entity.getId(), id);
            fileMapper.insertFile(entity);
        }

        log.info("此文件已上传： {}", file.getOriginalFilename());
    }

    @Override
    public long uploadFilePlus(MultipartFile file, Long id, String uploadType) throws IOException {
        String fileName = DateFormatUtils.format(new Date(), "yyyyMMddHHmmssSSS") + file
            .getOriginalFilename()
            .substring(file
                           .getOriginalFilename()
                           .lastIndexOf('.'));
        saveFile2Disk(file, generatePath(fileName));

        com.chinatechstar.file.entity.File entity = getFileEntity(file,
                                                                  fileName,
                                                                  imageServerBaseurl);

        long fileId = entity.getId();

        if ("subscribeLogo".equals(uploadType)) {
            List<LinkedHashMap<String, Object>> list = fileMapper.queryFileSysUserByFileId(String.valueOf(
                id));
            fileMapper.insertFileSysUser(sequenceGenerator.nextId(), fileId, id);
            fileMapper.insertFile(entity);
        } else {
            fileMapper.insertFileSysUser(sequenceGenerator.nextId(), fileId, id);
            fileMapper.insertFile(entity);
        }
        String url = imageServerBaseurl + fileName;
        Map<String, Object> map = new HashMap<>();
        map.put("id", fileId);
        map.put("url", url);
        fileMapper.updateFileBy(map);

        log.info("此文件已上传： {}", file.getOriginalFilename());

        return fileId;
    }

    /**
     * 上传用户头像
     *
     * @param file
     * @param userId
     * @return
     * @throws IOException
     */
    @Override
    public String uploadProfile(MultipartFile file, String userId) throws IOException {
        String fileName = DateFormatUtils.format(new Date(), "yyyyMMddHHmmssSSS") + file
            .getOriginalFilename()
            .substring(file
                           .getOriginalFilename()
                           .lastIndexOf('.'));
        saveFile2Disk(file, generateProfileFilePath(fileName));

        com.chinatechstar.file.entity.File entity = getFileEntity(file,
                                                                  fileName,
                                                                  imageServerBaseurl);

        fileMapper.insertFileSysUser(sequenceGenerator.nextId(),
                                     entity.getId(),
                                     Long.valueOf(userId));
        fileMapper.insertFile(entity);

        String avatarUrl = imageServerBaseurl + fileName;
        sysUserDetailServiceClient.updateAvatar(avatarUrl, Long.valueOf(userId));

        log.info("此文件已上传： {}", avatarUrl);
        return avatarUrl;
    }

    /**
     * 上传农癌初始数据
     *
     * @param file
     * @param userId
     * @return
     * @throws IOException
     */
    @Override
    public String uploadFile4DataInit(MultipartFile file, String userId) throws IOException {
        // 生成新的文件名称
        String fileName =
            DateFormatUtils.format(new Date(), "yyyyMMddHHmmssSSS") + "_" + userId + "_" + file
                .getOriginalFilename()
                .substring(0,
                           file
                               .getOriginalFilename()
                               .lastIndexOf('.')) + file
                .getOriginalFilename()
                .substring(file
                               .getOriginalFilename()
                               .lastIndexOf('.'));

        // 生成文件的存放路径
        String path = generateCerDataInitFileSavePath(fileName);

        // 将文件保存到磁盘
        saveFile2Disk(file, path);

        return path;
    }

    @Override
    public String uploadFile4DetailReport(MultipartFile file, String userId) throws IOException {
        // 生成新的文件名称
        String fileName =
            DateFormatUtils.format(new Date(), "yyyyMMddHHmmssSSS") + "+" + userId + "+" + file
                .getOriginalFilename()
                .substring(0,
                           file
                               .getOriginalFilename()
                               .lastIndexOf('.')) + file
                .getOriginalFilename()
                .substring(file
                               .getOriginalFilename()
                               .lastIndexOf('.'));

        // 生成文件的存放路径
        String path = generateCerDetailReportFileSavePath(fileName);

        // 将文件保存到磁盘
        saveFile2Disk(file, path);

        return path;
    }

    /**
     * 上传内镜诊断图片
     *
     * @param file
     * @param userId
     * @return
     * @throws IOException
     */
    @Override
    public String uploadFile4EndoscopicDiagnosis(MultipartFile file, String userId)
        throws IOException {

        String fileName = file
            .getOriginalFilename()
            .substring(0,
                       file
                           .getOriginalFilename()
                           .lastIndexOf('.')) + "_" + UuidUtil.get32UUID() + file
            .getOriginalFilename()
            .substring(file
                           .getOriginalFilename()
                           .lastIndexOf('.'));

        String path = generatCerEndoscopicDiagnosisFileSavePath(fileName);
        saveFile2Disk(file, path);
        return path;
    }

    /**
     * 上传内镜诊断报告单
     *
     * @param file
     * @param userId
     * @return
     * @throws IOException
     */
    @Override
    public String uploadFile4Report(MultipartFile file, String userId) throws IOException {
        String fileName = file
            .getOriginalFilename()
            .substring(0,
                       file
                           .getOriginalFilename()
                           .lastIndexOf('.')) + "_" + UuidUtil.get32UUID() + file
            .getOriginalFilename()
            .substring(file
                           .getOriginalFilename()
                           .lastIndexOf('.'));

        String path = generatReportFileSavePath(fileName);
        saveFile2Disk(file, path);
        return path;
    }


    @Override
    public List<LinkedHashMap<String, Object>> queryFileTypeName() {
        return sysDictServiceClient.queryDictByDictType("file");
    }

    @Override
    public void addFolder(Long id, String originalFilename, Long parentId) {
        com.chinatechstar.file.entity.File entity = new com.chinatechstar.file.entity.File();
        entity.setName(originalFilename);
        entity.setOriginalFilename(originalFilename);
        entity.setType((short) 1);
        entity.setId(sequenceGenerator.nextId());
        entity.setCreateTime(new Date(System.currentTimeMillis()));
        entity.setParentId(parentId);
        entity.setContentType("folder");
        entity.setTenantCode(CurrentUserUtils
                                 .getOAuth2AuthenticationDetailsInfo()
                                 .get("tenantCode"));// 当前用户的租户编码
        fileMapper.insertFileSysUser(sequenceGenerator.nextId(), entity.getId(), id);
        fileMapper.insertFile(entity);

        log.info("此文件夹已新增： {}", originalFilename);
    }

    @Override
    public String uploadCerAttachmentFile(MultipartFile file, String userId) throws IOException {
        // 获取文件的名称
        String fileName = file.getOriginalFilename();
        // 生成文件存放个路径
        String path = generateCerAttachmentFileSavePath(fileName);
        // 写入磁盘
        saveFile2Disk(file, path);

        return path;
    }

    @Override
    public String saveCerAttachmentFile(String path, String fileName) throws IOException {
        // 将path按照/进行拆分
        String[] arr = path.split("/");

        // 文件前缀
        String prefix = arr[arr.length - 3] + "/" + arr[arr.length - 2];

        // 将附件信息写附件表中
        CerAttachmentInfo plotAttachmentInfo = getAttachmentInfo(fileName, path, prefix);

        // 写入数据库
        cerAttachmentInfoMapper.insert(plotAttachmentInfo);

        return plotAttachmentInfo.getId();
    }

    /**
     * 获取附件实体
     *
     * @param fileName
     * @param path
     * @param prefix
     * @return
     */
    private static CerAttachmentInfo getAttachmentInfo(String fileName,
                                                       String path,
                                                       String prefix) {
        CerAttachmentInfo plotAttachmentInfo = new CerAttachmentInfo();
        plotAttachmentInfo.setId(UuidUtil.get32UUID());
        plotAttachmentInfo.setFileName(fileName);
        plotAttachmentInfo.setFilePath(path);
        plotAttachmentInfo.setFileUrl(prefix + "/" + fileName);
        plotAttachmentInfo.setFileType("");
        plotAttachmentInfo.setInsertDatetime(new Date(System.currentTimeMillis()));
        plotAttachmentInfo.setUpdateDatetime(new Date(System.currentTimeMillis()));
        return plotAttachmentInfo;
    }

    private String generateCerAttachmentFileSavePath(String fileName) {
        // 生成一个uuid
        String uuid = UuidUtil.get32UUID();
        // 每一个文件都单独存放于一个uuid文件夹下
        // 日期目录
        String dirName =
            cerAttachmentInDisk + DateFormatUtils.format(new Date(), "yyyyMMdd") + "/" + uuid;
        File dir = new File(dirName);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return dirName + "/" + fileName;
    }


    /**
     * 执行与表单相关的文件的删除
     *
     * @param id
     */
    private void execDelFormFiles(String id) {
        List<LinkedHashMap<String, Object>> list = fileMapper.queryFileById(id);
        if (list.size() > 0) {
            LinkedHashMap<String, Object> map = list.get(0);
            String url = (String) map.get("url");
            //1.删除硬盘上的文件
            deleteFileByPath(url);
            //2.删除zjcode_file_sysuser中的数据
            Long[] ids = new Long[]{Long.parseLong(id)};
            int i = fileMapper.deleteFile(ids);
            log.info("删除个数：{}", i);
        }
    }

    /**
     * 将文件写入磁盘
     *
     * @param file
     * @param path
     * @throws IOException
     */
    private void saveFile2Disk(MultipartFile file, String path) throws IOException {
        File f = new File(path);
        if (f.exists()) {
            f.delete();
        }
        //将文件写入磁盘
        Files.write(Paths
                        .get(path)
                        .normalize()
                        .toAbsolutePath(), file.getBytes(), StandardOpenOption.CREATE_NEW);
    }

    /**
     * 获取待插入的文件的实体
     *
     * @param file
     * @param fileName
     * @return
     */
    private com.chinatechstar.file.entity.File getFileEntity(MultipartFile file,
                                                             String fileName,
                                                             String url) {
        com.chinatechstar.file.entity.File entity = new com.chinatechstar.file.entity.File();
        entity.setName(file.getName());
        entity.setOriginalFilename(file.getOriginalFilename());
        entity.setFileSize(file.getSize());
        entity.setContentType(file.getContentType());
        entity.setUrl(url + fileName);
        entity.setId(sequenceGenerator.nextId());
        entity.setTenantCode(CurrentUserUtils
                                 .getOAuth2AuthenticationDetailsInfo()
                                 .get("tenantCode"));// 当前用户的租户编码
        entity.setCreateTime(new Date(System.currentTimeMillis()));
        return entity;
    }


    private com.chinatechstar.file.entity.File getFileEntity(MultipartFile file,
                                                             String fileName,
                                                             String contentType,
                                                             String basicDir) {
        String oFilename = file.getOriginalFilename();
        //文件后缀
        String suffix = oFilename.substring(oFilename.lastIndexOf("."));
        com.chinatechstar.file.entity.File entity = new com.chinatechstar.file.entity.File();
        entity.setName(suffix);
        entity.setOriginalFilename(oFilename);
        entity.setFileSize(file.getSize());
        entity.setContentType(contentType);
        entity.setUrl(basicDir + fileName);
        entity.setId(sequenceGenerator.nextId());
        entity.setCreateTime(new Date(System.currentTimeMillis()));
        entity.setTenantCode("");// 当前用户的租户编码
        return entity;
    }

    /**
     * 上传文件到与jar包同级的外部目录
     *
     * @param fileName 已修改成时间戳的文件名称
     * @return
     * @throws IOException
     */
    private String generatePath(String fileName) throws IOException {
        //File rootDirectoryPath = new File(ResourceUtils
        //                                      .getURL("classpath:")
        //                                      .getPath());
        //if (!rootDirectoryPath.exists()) {
        //    rootDirectoryPath = new File("");
        //}
        //File uploadPath = new File(rootDirectoryPath.getAbsolutePath(), "static/upload/");
        //if (!uploadPath.exists()) {
        //    uploadPath.mkdirs();
        //}
        //return rootDirectoryPath.getAbsolutePath() + "/static/upload/" + fileName;
        return imageUploadDir + fileName;
    }


    /**
     * 生成头像文件的上传位置
     *
     * @param fileName
     * @return
     * @throws IOException
     */
    private String generateProfileFilePath(String fileName) throws IOException {

        return profileUploadDir + fileName;
    }

    /**
     * 生成农癌初始数据上报文件的存放路径
     *
     * @param fileName
     * @return
     */
    private String generateCerDataInitFileSavePath(String fileName) {
        // 日期目录
        String dirName = cerDataInitFileInDisk + DateFormatUtils.format(new Date(), "yyyyMMdd");
        File dir = new File(dirName);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return dirName + "/" + fileName;
    }

    /**
     * 生成详情数据上报模块中,上传的文件的存放路径
     * @param fileName
     * @return
     */
    private String generateCerDetailReportFileSavePath(String fileName) {
        // 日期目录
        String dirName = cerDetailReportFileInDisk + DateFormatUtils.format(new Date(), "yyyyMMdd");
        File dir = new File(dirName);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return dirName + "/" + fileName;
    }

    /**
     * 生成内镜斩断文件的存放路径
     *
     * @param fileName
     * @return
     */
    private String generatCerEndoscopicDiagnosisFileSavePath(String fileName) {
        // 日期目录
        String dirName =
            cerEndoscopicDiagnosisFileInDisk + DateFormatUtils.format(new Date(), "yyyyMMdd");
        File dir = new File(dirName);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return dirName + "/" + fileName;
    }

    /**
     * 生成内镜诊断报告单的存放路径
     *
     * @param fileName
     * @return
     */
    private String generatReportFileSavePath(String fileName) {
        // 日期目录
        String dirName = cerReportFileInDisk + DateFormatUtils.format(new Date(), "yyyyMMdd");
        File dir = new File(dirName);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return dirName + "/" + fileName;
    }


    /**
     * 删除硬盘上的文件
     *
     * @param urlList
     */
    private void deleteFileInDisk(List<String> urlList) {
        urlList.forEach(path -> {
            deleteFileByPath(path);
        });
    }

    /**
     * 按照给定的路径删除对应的文件
     *
     * @param path
     */
    private void deleteFileByPath(String path) {
        try {
            if (path != null) {
                Files.deleteIfExists(Paths.get(path));// 删除磁盘里的文件
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
